/*
Copyright (C) 2022-2024 Inspur Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/
package cryptoprovider

/**
 * Bcrypt 密码加密验证器
 * 加密：前端使用rsa公钥对密码加密，后端使用rsa私钥解密后，bcrypt对明文进行bcrypt加密hash后，存入数据库
 * 验证：前端使用rsa公钥对密码进行加密，后端使用rsa私钥解密后，bcrypt对明文进行bcrypt加密hash，然后与数据库中的密码进行比较
 */

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"icep/common/logger"

	"golang.org/x/crypto/bcrypt"
)

var log = logger.GetLogger("common.rdb")

const (
	// 经base64加密后的rsa公钥
	defaultRSAPublicKey = "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFwWGNOZ3NzMlVSWVNTV0Z1QlJieQpQeVkrU2FHTXVvRk04L0l1ZkdYV251Ylg0RWYvUkVSSlFudXlQRitrcHAxUjRmOXREQk8zSjlvdGk0OUJqTGVzCm8wN2czeGdmcWoxcmVpL1FlNlVoYnVsTkViV2l0M2RrNGZxV1lHQllOUkJNUnNqZDRtMGtpRmtPNk01d29MTEoKZlU3aXAzVlpQa2JlL1RuRENpbjhTQzB0bE9mdmFzRUdHU2hReTlQWlg1anpJK3R5aW5wSlRKTm1zMnY2RWVPYgpJTXpCVm9RUTNrRnpmcXVsNFo3bWwwaTFMMzk0RjlJQXRXNk9QeUNhUlowN3pKVThISUVicVo2Rndwek5mSzVqCjE0SlpsOE0wU1ZFbHVWWklZZmY4Y05MKzhZUms1WmM4STIzeXp6UVkxWXBxVnlORmU2TU1jZGVmaStHeUhBaVEKWVFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="
	// 经base64加密后的rsa私钥
	defaultRSAPrivateKey = "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBcFhjTmdzczJVUllTU1dGdUJSYnlQeVkrU2FHTXVvRk04L0l1ZkdYV251Ylg0RWYvClJFUkpRbnV5UEYra3BwMVI0Zjl0REJPM0o5b3RpNDlCakxlc28wN2czeGdmcWoxcmVpL1FlNlVoYnVsTkViV2kKdDNkazRmcVdZR0JZTlJCTVJzamQ0bTBraUZrTzZNNXdvTExKZlU3aXAzVlpQa2JlL1RuRENpbjhTQzB0bE9mdgphc0VHR1NoUXk5UFpYNWp6SSt0eWlucEpUSk5tczJ2NkVlT2JJTXpCVm9RUTNrRnpmcXVsNFo3bWwwaTFMMzk0CkY5SUF0VzZPUHlDYVJaMDd6SlU4SElFYnFaNkZ3cHpOZks1ajE0SlpsOE0wU1ZFbHVWWklZZmY4Y05MKzhZUmsKNVpjOEkyM3l6elFZMVlwcVZ5TkZlNk1NY2RlZmkrR3lIQWlRWVFJREFRQUJBb0lCQUdnZVZSTWpla2RsNHhYbgp5QXBOMlJ1aG5mcEpQTHV6VjAvOTZnQi9PS0QrMDRLZTYyTHlzVUt6Z1lGNkVhcUNQaDgrMGtpU00yTlJaKzEwCnRLT3VoR1N5NHMrZlozV0JkcHNKY1dBTWxkQXI0RUl2a0hkd0dyd1VLZ0gxUGl3TjJHR0dUaEVINnNBZGdrTSsKZkNLeXZoVXF5WGJrWWNiTlk4M21SZk9rWGNxd0YyWjlhRmV5TmNweHRiVmdsUVd6cHJQaTBiZGQ2MUlXNjJkNwpITUdUUlNuNUlJL0hNUFNOMWVZNjAyYzN6VGpNZSsrSGQybUI4V0JNb0VVMkQ4WDAyUGMxVERCNHdUNmtjcTdyCjJFcGI0Y2NmS1FpZ0FFUllKSGNmbXpybUhkcStxYUhROHplRm12S3NObUE0bGhRazhYMVZRUVBOcitYcnhpWUEKVk16Z1lGMENnWUVBM0lsZGJRNlkxY1NXZEJwZlNKdzV1SmlZZUxldDJzb1dvdk11Y3VxcEpQV2xsRjNsLzVEVgo0OTR5V3hOc3hTNElKc0Nkd0tGbEx5TnNoWitmbzFLdTZkNkd0R2NKSHJuckZNWlZuVTBRc3QwWDg2bG1YOE1TCkw0WE41aEVhNzE2R3FOZGRwVHVLM0FRRzFDRmtjQjRiVjBzeG5oUWNCZ09hK3BhaCtZMW16R2NDZ1lFQXdCS2IKUzhHYk04dmhCQVpVNWI2N0MyU3ZIcFdaVk9wTndDUkxDTkpyRzF4UFpHTlYrUXRUOGE3N29iYVVsUWhHN1ZsWgowVlNQVForendEZWZMWDFsZWtZRXVjVHg1QVcxam1CdnNUZmp5YnB6b21DQThPM0RaZmcySEdueVc0TldaWTV2CjJDYVlTRFYxTkhERlc4eGtHR0NiTy9FNC92aXJ4a09JREZxVVAvY0NnWUF1MWNWUGlkQVBaZWFGMWF2eW9hcm8KZHcrNlkxZTZwVHhWY3N1a2dBMVgyZ3RHRFFMeFdYTHpKNWNPSEh2M0RDV1RCVWo3UmN0VlRYS1lsaU95N2JaVAoySHhIQjdLNkljVzQ3YjMwNEE3eHpWNXUwc1Q1QlZaeFhUc3k1dU1QZnNXZVNjNkxicSs0dGVvMVZyb2J6V2tGCkN1VUlxMENnVGxlMWJCUU9DV2VSZFFLQmdHWm5SUzdOcVM2amEzT1c0SnFnNXQ1N0xyYVNzZTVKQVcwSXJJeW0KaXY1S1ovV3cxSHgwSXF3L1M5OTdGWnFqa0JVcFJ5N0k4NkpDSERiR01QS3k0WENnZGhLSlF5SDhQVUVNZjJBdgoyUjJMYisxSm9GVlRucEwyRS90d0xRdkhhVCs2QWoxdXprL3BXSFMrRHlzVFBPYVdVdzFSWGI3cTI4Slg3T2dSClB1c2ZBb0dCQU5ja3dPYlZ1Q3psRXBVb0M5UFpWbkE2aEtvSFJORm9JSUkwdVo5WVhqN3lVTzVTMG1Rb2ZjbDYKdURuSTRDM29OTDZlWHdXRVBCTFJkQU5BUGc2ZStsa2dPM3pSWVJLZlI1QXF1YUdweXZ5QksydFRrWmFKVGZIbwphdWRtWUhvV3hOejE4ellsemJsbml3NDF1eUt6eXBhUlFTcXE5V2M4Ky8vczFEMDluNjFlCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg=="
)

type Bcrypt struct {
	pubKey     []byte
	privateKey []byte
	pk         *rsa.PrivateKey
}

func NewBcrypt(pubKey, privKey []byte) *Bcrypt {
	if pubKey == nil {
		pubKey = []byte(defaultRSAPublicKey)
	}
	if privKey == nil {
		privKey = []byte(defaultRSAPrivateKey)
	}
	pk, _ := x509.ParsePKCS1PrivateKey(privKey)
	crypt := &Bcrypt{
		pk: pk,
	}
	crypt.pubKey, _ = base64.StdEncoding.DecodeString(string(pubKey))
	crypt.privateKey, _ = base64.StdEncoding.DecodeString(string(privKey))

	var err error
	crypt.pk, err = getPrivKey(crypt.privateKey)
	if err != nil {
		log.Errorf("failed to get privateKey from %s. error: %v", string(privKey), err)
	}

	return crypt
}

func (crypt *Bcrypt) Encrypt(cipherPlain string) string {
	cipherPlainBytes, _ := base64.StdEncoding.DecodeString(cipherPlain)
	plainTxt, err := rsa.DecryptPKCS1v15(rand.Reader, crypt.pk, cipherPlainBytes)
	if err != nil {
		log.Errorf("failed to decrypt attempCipher[%s]. err: %v", cipherPlain, err)
		return cipherPlain
	}
	cipher, err := bcrypt.GenerateFromPassword([]byte(plainTxt), bcrypt.DefaultCost)
	if err != nil {
		log.Errorf("failed to generate bcrypt cipher. error: %v", err)
	}
	return string(cipher)
}

func (crypt *Bcrypt) Check(originCipher, attempCipher string) bool {
	attempCipherBytes, _ := base64.StdEncoding.DecodeString(attempCipher)
	plainTxt, err := rsa.DecryptPKCS1v15(rand.Reader, crypt.pk, attempCipherBytes)
	if err != nil {
		log.Errorf("failed to decrypt attempCipher[%s]. err: %v", attempCipher, err)
		return false
	}
	err = bcrypt.CompareHashAndPassword([]byte(originCipher), plainTxt)
	if err != nil {
		return false
	}
	return true
}

func (crypt *Bcrypt) Decrypt(cipher string) (string, error) {
	cipherBytes, _ := base64.StdEncoding.DecodeString(cipher)
	plainTxt, err := rsa.DecryptPKCS1v15(rand.Reader, crypt.pk, cipherBytes)
	if err != nil {
		return "", err
	}
	return string(plainTxt), nil

}

func getPrivKey(privKey []byte) (*rsa.PrivateKey, error) {
	block, _ := pem.Decode(privKey)
	if block == nil {
		return nil, errors.New("private rsaKey error!")
	}
	return x509.ParsePKCS1PrivateKey(block.Bytes)
}
